/**
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "plugins/ecmascript/runtime/ecma_call_profiling_table.h"
#include "plugins/ecmascript/runtime/js_object.h"

namespace ark::ecmascript {

EcmaCallProfilingTable::EcmaCallProfilingTable(uint32_t size)
{
    // We need to save std::numeric_limits<uint16_t>::max() value to indentify MEGAMORPHIC profile
    // And we need to reserve first element in table to indentify UNKNOWN profile
    ASSERT(size <= std::numeric_limits<uint16_t>::max() - 2U);
    callProfilingTable_.resize(size + 1);
    callProfilingBitMap_.resize(size + 1);
    callProfilingBitMap_[0] = true;
}

void EcmaCallProfilingTable::ClearObject([[maybe_unused]] uint16_t idx)
{
    UNREACHABLE();
}

std::optional<uint16_t> EcmaCallProfilingTable::InsertNewObject(ECMAObject *jsFunc)
{
    os::memory::LockHolder holder(tableLock_);
    // find first free elem
    uint32_t idx = 1;
    for (; idx < callProfilingBitMap_.size(); idx++) {
        if (!callProfilingBitMap_[idx]) {
            break;
        }
    }
    if (idx == callProfilingBitMap_.size()) {
        LOG(DEBUG, INTERPRETER) << "CallProfileTable: Table is full. Don't insert object " << std::hex << jsFunc;
        return std::nullopt;
    }
    ASSERT(idx != 0);
    LOG(DEBUG, INTERPRETER) << "CallProfileTable: Insert object " << std::hex << jsFunc << std::dec << " in slot "
                            << idx;
    ASSERT(callProfilingTable_[idx] == nullptr);
    callProfilingBitMap_[idx] = true;
    callProfilingTable_[idx] = jsFunc;
    return idx;
}

ECMAObject *EcmaCallProfilingTable::GetObject(uint16_t idx) const
{
    ASSERT(idx != 0);
    os::memory::LockHolder holder(tableLock_);
    return callProfilingTable_[idx];
}

uintptr_t EcmaCallProfilingTable::GetObjectPtr(uint16_t idx) const
{
    ASSERT(idx != 0);
    os::memory::LockHolder holder(tableLock_);
    return reinterpret_cast<uintptr_t>(&callProfilingTable_[idx]);
}

void EcmaCallProfilingTable::Sweep(const GCObjectVisitor &visitor)
{
    os::memory::LockHolder holder(tableLock_);
    for (size_t i = 1; i < callProfilingTable_.size(); ++i) {
        auto *object = callProfilingTable_[i];
        if (object == nullptr) {
            continue;
        }
        ASSERT(!object->IsForwarded());
        if (visitor(object) == ObjectStatus::DEAD_OBJECT) {
            LOG(DEBUG, GC) << "CallProfileTable: delete js function " << std::hex << object << std::dec << " in slot "
                           << i;
            callProfilingTable_[i] = nullptr;
        }
    }
}

void EcmaCallProfilingTable::UpdateMoved()
{
    os::memory::LockHolder holder(tableLock_);
    for (size_t i = 1; i < callProfilingTable_.size(); ++i) {
        auto *object = callProfilingTable_[i];
        if (object == nullptr) {
            continue;
        }
        if (object->IsForwarded()) {
            ObjectHeader *fwdObject = ark::mem::GetForwardAddress(object);
            callProfilingTable_[i] = static_cast<ECMAObject *>(fwdObject);
            LOG(DEBUG, GC) << "CallProfileTable: forward " << std::hex << object << " -> " << fwdObject << std::dec
                           << " in slot " << i;
        }
    }
}

void EcmaCallProfilingTable::VisitRoots(const GCRootVisitor &visitor)
{
    os::memory::LockHolder holder(tableLock_);
    for (size_t i = 1; i < callProfilingTable_.size(); ++i) {
        auto *object = callProfilingTable_[i];
        if (object == nullptr) {
            continue;
        }
        visitor(mem::GCRoot(mem::RootType::ROOT_VM, object));
    }
}

}  // namespace ark::ecmascript
